import { useState, useEffect, useCallback, useRef } from 'react'
import { useCache } from './useCache'

interface FetchOptions {
  ttl?: number
  staleWhileRevalidate?: boolean
  retryCount?: number
  retryDelay?: number
  timeout?: number
  headers?: Record<string, string>
  method?: string
  body?: string
}

interface FetchState<T> {
  data: T | null
  loading: boolean
  error: Error | null
  isStale: boolean
}

export function useCachedFetch<T>(
  url: string | null,
  options: FetchOptions = {}
) {
  const {
    ttl = 5 * 60 * 1000, // 5 minutes
    staleWhileRevalidate = true,
    retryCount = 3,
    retryDelay = 1000,
    timeout = 10000,
    headers = {},
    method = 'GET',
    body
  } = options

  const [state, setState] = useState<FetchState<T>>({
    data: null,
    loading: false,
    error: null,
    isStale: false
  })

  const cache = useCache<T>({ defaultTTL: ttl })
  const abortController = useRef<AbortController>()
  const requestId = useRef(0)

  const createCacheKey = useCallback((url: string, options: FetchOptions) => {
    const key = `${method}:${url}`
    if (body) {
      return `${key}:${btoa(body)}`
    }
    return key
  }, [method, body])

  const fetchWithRetry = useCallback(async (
    url: string,
    options: RequestInit,
    retries: number
  ): Promise<Response> => {
    try {
      const response = await fetch(url, options)
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`)
      }
      
      return response
    } catch (error) {
      if (retries > 0 && !options.signal?.aborted) {
        await new Promise(resolve => setTimeout(resolve, retryDelay))
        return fetchWithRetry(url, options, retries - 1)
      }
      throw error
    }
  }, [retryDelay])

  const fetchData = useCallback(async (
    url: string,
    forceRefresh = false,
    currentRequestId: number
  ) => {
    const cacheKey = createCacheKey(url, options)

    // Check cache first
    if (!forceRefresh) {
      const cachedData = cache.get(cacheKey)
      if (cachedData) {
        setState(prev => ({
          ...prev,
          data: cachedData,
          loading: staleWhileRevalidate,
          error: null,
          isStale: staleWhileRevalidate
        }))
        
        if (!staleWhileRevalidate) {
          return
        }
      }
    }

    // Set loading state
    setState(prev => ({
      ...prev,
      loading: true,
      error: null
    }))

    // Cancel previous request
    if (abortController.current) {
      abortController.current.abort()
    }

    abortController.current = new AbortController()
    const timeoutId = setTimeout(() => {
      abortController.current?.abort()
    }, timeout)

    try {
      const response = await fetchWithRetry(url, {
        method,
        headers: {
          'Content-Type': 'application/json',
          ...headers
        },
        body,
        signal: abortController.current.signal
      }, retryCount)

      clearTimeout(timeoutId)

      // Check if this request is still current
      if (currentRequestId !== requestId.current) {
        return
      }

      const result = await response.json()
      
      setState({
        data: result,
        loading: false,
        error: null,
        isStale: false
      })
      
      cache.set(cacheKey, result, ttl)
      
    } catch (error) {
      clearTimeout(timeoutId)
      
      // Check if this request is still current
      if (currentRequestId !== requestId.current) {
        return
      }

      // Don't set error state if request was aborted
      if (error instanceof Error && error.name === 'AbortError') {
        return
      }

      setState(prev => ({
        ...prev,
        loading: false,
        error: error as Error
      }))
    }
  }, [
    createCacheKey,
    options,
    cache,
    staleWhileRevalidate,
    method,
    headers,
    body,
    timeout,
    fetchWithRetry,
    retryCount,
    ttl
  ])

  useEffect(() => {
    if (!url) {
      setState({
        data: null,
        loading: false,
        error: null,
        isStale: false
      })
      return
    }

    const currentRequestId = ++requestId.current
    fetchData(url, false, currentRequestId)

    return () => {
      if (abortController.current) {
        abortController.current.abort()
      }
    }
  }, [url, fetchData])

  const refetch = useCallback(() => {
    if (!url) return Promise.resolve()
    
    const currentRequestId = ++requestId.current
    return fetchData(url, true, currentRequestId)
  }, [url, fetchData])

  const mutate = useCallback((newData: T | ((prevData: T | null) => T)) => {
    if (!url) return

    const cacheKey = createCacheKey(url, options)
    const updatedData = typeof newData === 'function' 
      ? (newData as (prevData: T | null) => T)(state.data)
      : newData

    setState(prev => ({
      ...prev,
      data: updatedData,
      isStale: false
    }))
    
    cache.set(cacheKey, updatedData, ttl)
  }, [url, createCacheKey, options, state.data, cache, ttl])

  const invalidate = useCallback(() => {
    if (!url) return

    const cacheKey = createCacheKey(url, options)
    cache.clear(cacheKey)
  }, [url, createCacheKey, options, cache])

  return {
    ...state,
    refetch,
    mutate,
    invalidate,
    cacheStats: cache.getStats()
  }
}
